package treeship.core.algorithm;

public class EICG {

    private static EICG singleton = new EICG();
    private long a;
    private long m;
    private long contatore;
    private long seme;
    /*
     *Utilizzo della classe
     *
     *EICG prevede due tipologie diverse di utilizzo
     *1° Normale generatore di numeri pseudocasuali
     *Si inizializza con new EICG();
     *si imposta un seme con setSeed(long seed);
     *e successivamente si possono estrarre reali [0,1[ con getNextDouble()
     *interi [0,max[ con getNextInt(long max)
     *o interi [0,2^31-1[ con getNext()
     *
     *2° Macchina senza stato di trasformazione random normale e inversa
     *
     *per questa funzione si usano
     *
     *getRand (long seed); per avere il numero random corrispondente a seed
     *
     *getSeed (long rand); per avere il seme corrispondente a rand
     *
    /*Buoni valori per i parametri sono
     *m=2^31-1
     *a=1288490188
     */

//Funzioni con i parametri cablati
    private long inverse(long x) {
        return inverse(x, m);
    }

    /* non viene mai usato
    private long mod(long x){
    return mod(x,m);
    }
     */
    public long getRand(long seed) {
        return inverse(a * seed);
    }

    public static long staticGetRand(long seed) {
        return getSingleton().getRand(seed);
    }

    public long getRandInt(long seed, long max) {
        return (long) max * getRand(seed) / m;
    }

    public static long staticGetRandInt(long seed, long max) {
        return getSingleton().getRandInt(seed, max);
    }

    public double getRandDouble(long seed) {
        return (double) getRand(seed) / m;
    }

    public static double staticGetRandDouble(long seed) {
        return getSingleton().getRandDouble(seed);
    }

    public long getNext() {
        contatore++;
        return getRand(seme + contatore);
    }

    public double getNextDouble() {
        return (double) getNext() / m;
    }

    public long getNextInt(long max) {
        return (long) max * getNext() / m;
    }

    public long getSeed(long randnum, long i) {
        return getSeed(randnum, a, m) - i;
    }

    public void setSeed(long seed) {
        seme = seed;
    }

    public long getSeed() {
        return seme;
    }

    public long getContatore() { //restituisce l'indice dell'attuale numero dato
        return contatore;
    }

//Funzioni senza parametri cablati
    private static long inverse(long x, long modulo) {
        long a = mod(x, modulo);
        if (a == 0) {
            return 0;
        }
        long n = modulo;
        long q;
        long r = 1;
        long pi2 = 0;
        long qi2 = 0;
        long pi1 = 1;
        long qi1 = 0;
        long pi;

        while (r != 0) {
            q = n / a;
            r = n - q * a;
            pi = mod(pi2 - pi1 * qi2, modulo);
            //i=i+1, aggiornamenti per il ciclo successivo			
            n = a;
            a = r;
            pi2 = pi1;
            pi1 = pi;
            qi2 = qi1;
            qi1 = q;
        }
        pi = mod(pi2 - pi1 * qi2, modulo);
        return pi;
    }

    private static long mod(long x, long modulo) {
        if (x < 0) {
            long resto = x % modulo;
            if (resto == 0) {
                return 0;
            } else {
                return modulo + resto;
            }
        } else {
            return x % modulo;
        }
    }

    public static long getRand(long seed, long a_molt, long modulo) {
        return inverse(a_molt * seed, modulo);
    }

    /*
     *da rifare
     *inv=ax mod m
     *1=h*a+k*m
     *x=inv*h
     *
     *mi sa che h e' inverse(a)
     *quindi
     *
     *x=inverse(randnum)*inverse(a)
     *
     */
    public long getSeed(long randnum, long a_molt, long modulo) {
        long k = 0;
        long invrandnum = inverse(randnum);
        while (mod((invrandnum + k * modulo), a_molt) != 0) {
            k++;
        }
        return (invrandnum + k * modulo) / a_molt;
    }

    public long getSeed(long randnum, long i, long a_molt, long modulo) {
        return getSeed(randnum, a_molt, modulo) - i;
    }

    public EICG(long parA, long parM) {
        a = parA;
        m = parM;
        contatore = 0;
    }

    public EICG(long seed, long parA, long parM) {
        a = parA;
        m = parM;
        seme = seed;
        contatore = 0;
    }

    public EICG() {
        a = 1288490188;
        m = 2147483647;
        contatore = 0;
    }

    public static EICG getSingleton() {
        return singleton;
    }
    /*	
    public static void main(String args[]){
    }
    //Funzioni di test
    private static void test(){
    long result;
    long maxx=50000000;
    m=11;
    System.out.println ("Start!");
    for (long i=0;i<maxx;i++){
    result=i%m;
    }
    System.out.println ("Stop!");
    System.out.println ("Start!");
    for (long i=0;i<maxx;i++){
    result=mod(i,m);
    }
    System.out.println ("Stop!");
    }
    public static void test2(){
    for (int i=0;i<29;i++){
    System.out.println (inverse(i,29));
    }
    }
    public static void test3(){
    m=26;
    System.out.println (inverse(15));		
    }
    public static void test4(){
    contatore=0;
    a=1734;
    m=71;
    setSeed(25);
    for (int i=0;i<100;i++){
    System.out.print (getNext()+" ");
    }
    }
    public static void test5(){
    EICG ran=new EICG();
    ran.setSeed(0);
    //float contatore=0;
    int a0=0;
    int a1=0;
    int a2=0;
    int a3=0;
    int a4=0;
    int a5=0;
    int a6=0;
    int a7=0;
    int a8=0;
    int a9=0;
    long massimo=100000;
    for (int i=0;i<massimo;i++){
    //			System.out.println (ran.getNextDouble()+" ");
    double x=ran.getNextDouble();
    //			System.out.print (x+" ");
    if ((0<=x) && (x<0.1)) {a0++;}
    if ((0.1<=x) && (x<0.2)) {a1++;}
    if ((0.2<=x) && (x<0.3)) {a2++;}
    if ((0.3<=x) && (x<0.4)) {a3++;}
    if ((0.4<=x) && (x<0.5)) {a4++;}
    if ((0.5<=x) && (x<0.6)) {a5++;}
    if ((0.6<=x) && (x<0.7)) {a6++;}
    if ((0.7<=x) && (x<0.8)) {a7++;}
    if ((0.8<=x) && (x<0.9)) {a8++;}
    if ((0.9<=x) && (x<1)) {a9++;}
    }	
    //System.out.println(contatore/10000);
    System.out.println("poppo");
    System.out.println((double)a0/massimo);
    System.out.println((double)a1/massimo);
    System.out.println((double)a2/massimo);
    System.out.println((double)a3/massimo);
    System.out.println((double)a4/massimo);
    System.out.println((double)a5/massimo);
    System.out.println((double)a6/massimo);
    System.out.println((double)a7/massimo);
    System.out.println((double)a8/massimo);
    System.out.println((double)a9/massimo);
    }
     */
}
